//
// Created by gxx on 2023/11/19.
//
#include "util/file.h"
#include "util/logger.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

namespace TinyDB {

TFileUnixImpl::TFileUnixImpl(t_string file_name)
: file_name_(file_name),
  fp_(::fopen(file_name_.c_str(), "wb+")) {}

TFileUnixImpl::~TFileUnixImpl() {
  Close();
}

t_uint32 TFileUnixImpl::Read(t_byte *buf, t_uint32 len, t_uint64 offset) {
  if (!offset) {
    return 0;
  }
  if (!fp_) {
    LOG_INFO("%s is not open success.\n");
    return 0;
  }
  ::fseek(fp_, offset, SEEK_SET);
  t_uint32 n = ::fread(buf, sizeof(t_byte), len, fp_);
  if (!n) {
    t_uint32 err = ferror(fp_);
    LOG_ERROR("%s: %s", file_name_.c_str(), strerror(err));
    return 0;
  }
  return n;
}

t_void TFileUnixImpl::Write(t_byte *buf, t_uint32 len, t_uint64 offset) {
  if (!fp_) {
    LOG_INFO("%s is not open success.\n");
    return;
  }
  ::fseek(fp_, offset, SEEK_SET);
  t_uint32 wn = 0;
  t_uint32 remain = 0;
  t_uint32 n = 0;
  t_uint32 err;
  while (wn != len) {
    remain = len - wn;
    n = ::fwrite(buf, sizeof(t_byte), remain, fp_);
    if (n != remain) {
      err = ferror(fp_);
      if (err) {
        LOG_ERROR("%s: %s", file_name_.c_str(), strerror(err));
        break;
      }
    }
    wn += n;
  }
}

t_void* TFileUnixImpl::Mmap(t_uint32 len, t_uint64 offset) {
  if (!fp_) {
    LOG_INFO("%s is not open success.\n");
    return nullptr;
  }
  t_uint32 fd = fileno(fp_);
  t_void* res = ::mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
  return res;
}

t_void TFileUnixImpl::Unmap(t_byte *start, t_uint32 len) {
  t_uint32 fd = fileno(fp_);
  t_uint32 ret = ::munmap(start, len);
  if (ret) {
    LOG_INFO("file:%s Unmap is not success.", file_name_.c_str());
  }
}

t_void TFileUnixImpl::Close() {
  if (fp_ != nullptr) {
    ::fclose(fp_);
    fp_ = nullptr;
  }
}

t_void TFileUnixImpl::Flush() {
  if (fp_) {
    ::fflush(fp_);
  }
}

t_string TFileUnixImpl::GetFileName() {
  return file_name_;
}

t_uint64 TFileUnixImpl::GetFileSize() {
  struct stat st;
  if (stat(file_name_.c_str(), &st) != 0) {
    LOG_ERROR("File:%s can not get file size.", file_name_.c_str());
    return 0;
  }
  return static_cast<t_uint64>(st.st_size);
}

}

